using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Reflection;

namespace TR0217.ControlEx
{
    [ToolboxBitmap(typeof(ColorTable), "ColorTable.bmp")]
	public class ColorTable : LabelRotate
	{
		public event EventHandler SelectedIndexChanged;
		public Color SelectedItem
		{
			get
			{
				if (_selindex < 0 || _selindex >= _colors.Count)
					return Color.White;
				return _colors[_selindex];
			}
			set 
			{
				if (_selindex < _colors.Count && value == _colors[_selindex])
					return;
				int index = _colors.IndexOf(value);
				if (index < 0)
					return;
				SetIndex(index);
			}
		}
		public bool ColorExist(Color c)
		{
			int index = _colors.IndexOf(c);
			return index >= 0;
		}

		int _cols = 0;
		int _rows = 0;
        int _cstmColors = 0;
        int _cstmColorCount = 0;
		public int Cols
		{
			get { return _cols; }
			set
			{
				_cols = value;
				_rows = _colors.Count / _cols;
                _cstmColorCount = 0;
                int m = _colors.Count % _cols;
                if (m != 0)
                {
                    _cstmColorCount = _cols - m;
                    _rows++;
                }
                CalcSize();
			}
		}	
		Size _fieldSize = new Size(12, 12);
		public Size FieldSize
		{
			get { return _fieldSize; }
			set { _fieldSize = value; }
		}

        public new Padding Padding
        {
            get { return base.Padding; }
            set { return; }
        }

        public new Size Size
        {
            get { return base.Size; }
            set { return; }
        }

		int CompareColorByValue(Color c1, Color c2)
		{
			int color1 = c1.R << 16 | c1.G << 8 | c1.B;
			int color2 = c2.R << 16 | c2.G << 8 | c2.B;
			if (color1 > color2)
				return -1;
			if (color1 < color2)
				return 1;
			return 0;
		}
		int CompareColorByHue(Color c1, Color c2)
		{
			float h1 = c1.GetHue();
			float h2 = c2.GetHue();
			if (h1 < h2)
				return -1;
			if (h1 > h2)
				return 1;
			return 0;
		}
		int CompareColorByBrightness(Color c1, Color c2)
		{
			float h1 = c1.GetBrightness();
			float h2 = c2.GetBrightness();
			if (h1 < h2)
				return -1;
			if (h1 > h2)
				return 1;
			return 0;
		}

		public void SortColorByValue()
		{
			_colors.Sort(CompareColorByValue);
			Invalidate();
		}
		public void SortColorByHue()
		{
			_colors.Sort(CompareColorByHue);
			Invalidate();
		}
		public void SortColorByBrightness()
		{
			_colors.Sort(CompareColorByBrightness);
			Invalidate();
		}

		List<Color> _colors = new List<Color>();
		public ColorTable(Color[] colors)
		{
            base.Padding = new Padding(6);
			this.DoubleBuffered = true;
			if (colors != null)
				_colors = new List<Color>(colors);
			Cols = 16;
			_initialColorCount = _colors.Count;			
		}
		public ColorTable()
		{
			this.DoubleBuffered = true;
            base.Padding = new Padding(6);
			PropertyInfo[] propinfos = typeof(Color).GetProperties(BindingFlags.Public | BindingFlags.Static);
			foreach (PropertyInfo info in propinfos)
			{
				if (info.PropertyType == typeof(Color))
				{
					Color c = (Color)info.GetValue(typeof(Color), null);
					if (c.A == 0) // transparent
						continue;
					_colors.Add(c);
				}
			}
			_initialColorCount = _colors.Count;            
			Cols = 16;
		}

		public void RemoveCustomColor()
		{
			if (_colors.Count > _initialColorCount)
				_colors.RemoveAt(_colors.Count-1);
		}
		public void SetCustomColor(Color col)
		{
            if (_colors.Contains(col) == false)
            {
                if (_cstmColors == _cstmColorCount)
                    RemoveCustomColor();

                _colors.Add(col);
                Invalidate(GetRectangle(_colors.Count - 1));
            }
		}
		public Color[] Colors
		{
			get { return _colors.ToArray(); }
			set
			{
				_colors = new List<Color>(value);
                Cols = Cols;
				_initialColorCount = _colors.Count;
			}
		}

		int _selindex = 0;
		int _initialColorCount = 0;
		Rectangle GetSelectedItemRect()
		{
			Rectangle rect = GetRectangle(_selindex);
			rect.Inflate(6, 6);
			return rect;
		}
		Rectangle GetRectangle(int index)
		{
			int row = 0;
			int col = 0;
			GetRowCol(index, ref row, ref col);
			return GetRectangle(row, col);
		}
		void GetRowCol(int index, ref int row, ref int col)
		{
			row = index / _cols;
			col = index - (row * _cols);
		}
		Rectangle GetRectangle(int row, int col)
		{
            int x = Padding.Left + (col * (_fieldSize.Width + 3)) + 2;
            int y = Padding.Top + (row * (_fieldSize.Height + 3)) + 2;
			return new Rectangle(x,y,_fieldSize.Width, _fieldSize.Height);
		}
		int GetIndexFromMousePos(int x, int y)
		{
            int col = (x - Padding.Left) / (_fieldSize.Width + 3);
            int row = (y - Padding.Top) / (_fieldSize.Height + 3);
			return GetIndex(row, col);
		}
		int GetIndex(int row, int col)
		{
			if (col < 0 || col >= _cols)
				return -1;
			if (row < 0 || row >= _rows)
				return -1;
			return row * _cols + col;
		}
		void SetIndex(int index)
		{
			if (index == _selindex)
				return;
			Invalidate(GetSelectedItemRect());
			_selindex = index;
			if (SelectedIndexChanged != null)
				SelectedIndexChanged(this, null);
			Invalidate(GetSelectedItemRect());
		}
		protected override void OnMouseMove(MouseEventArgs e)
		{
			base.OnMouseMove(e);
		}
		protected override void OnMouseDown(MouseEventArgs e)
		{
			base.OnMouseDown(e);
			Focus();
			if (GetSelectedItemRect().Contains(new Point(e.X, e.Y)))
				return;
			int index = GetIndexFromMousePos(e.X, e.Y);
			if (index != -1)
				SetIndex(index);
		}

        int _tableWidth = 0;
        int _tableHeight = 0;

        void CalcSize()
        {
            _tableWidth = _cols * (_fieldSize.Width + 3);
            _tableHeight = _rows * (_fieldSize.Height + 3);
            Width = Padding.Left + Padding.Right + _tableWidth+1;
            Height = Padding.Top + Padding.Bottom + _tableHeight+1;
        }

		protected override void OnPaint(PaintEventArgs e)
		{
			base.OnPaint(e);
			int index = 0;

            Rectangle r = new Rectangle(Padding.Left, Padding.Top, _tableWidth, _tableHeight);
			e.Graphics.DrawRectangle(Pens.CadetBlue, r);
			r.X++;
			r.Y++;
			r.Width--;
			r.Height--;
			e.Graphics.FillRectangle(Brushes.White, r);

            for (int col = 1; col < _cols; col++)
            {
                int x = Padding.Left + (col * (_fieldSize.Width + 3));
                e.Graphics.DrawLine(Pens.CadetBlue, x, r.Y, x, r.Bottom - 1);
            }
            for (int row = 1; row < _rows; row++)
            {
                int y = Padding.Top + (row * (_fieldSize.Height + 3));
                e.Graphics.DrawLine(Pens.CadetBlue, r.X, y, r.Right - 1, y);
            }

            for (int row = 0; row < _rows; row++)
            {
                for (int col = 0; col < _cols; col++)
                {
                    if (index >= _colors.Count)
                        break;
                    Rectangle rect = GetRectangle(row, col);
                    using (SolidBrush brush = new SolidBrush(_colors[index++]))
                    {
                        e.Graphics.FillRectangle(brush, rect);
                    }
                }
            }

			if (_selindex >= 0)
			{
				Rectangle rect = GetSelectedItemRect();
				e.Graphics.FillRectangle(Brushes.White, rect);
				rect.Inflate(-3, -3);
				using (SolidBrush brush = new SolidBrush(SelectedItem))
				{
					e.Graphics.FillRectangle(brush, rect);
				}
				if (Focused)
				{
				rect.Inflate(2, 2);
					ControlPaint.DrawFocusRectangle(e.Graphics, rect);
				}
				else
				{
					rect.X -= 2;
					rect.Y -= 2;
					rect.Width += 3;
					rect.Height += 3;
					e.Graphics.DrawRectangle(Pens.CadetBlue, rect);
				}
			}
		}
		protected override void OnGotFocus(EventArgs e)
		{
			base.OnGotFocus(e);
			Invalidate();
		}
		protected override void OnLostFocus(EventArgs e)
		{
			base.OnLostFocus(e);
			Invalidate();
		}
		protected override bool ProcessDialogKey(Keys keyData)
		{
			bool processed = false;
			int row = 0;
			int col = 0;
			GetRowCol(_selindex, ref row, ref col);

			switch (keyData)
			{
				case Keys.Down:
					row++;
					processed = true;
					break;
				case Keys.Up:
					row--;
					processed = true;
					break;
				case Keys.Left:
					col--;
					if (col < 0)
					{
						col = _cols-1;
						row--;
					}
					processed = true;
					break;
				case Keys.Right:
					col++;
					if (col >= _cols)
					{
						col = 0;
						row++;
					}
					processed = true;
					break;
			}
			if (processed)
			{
				int index = GetIndex(row, col);
				if (index != -1)
					SetIndex(index);
				return false;
			}
			return base.ProcessDialogKey(keyData);
		}
	}
}
